package core

import (
	"fmt"
	"time"

	"gark-elastic-job/engine"
	"gark-elastic-job/etcd"
	"github.com/sirupsen/logrus"
)

// 作业(调度)节点
type JobNode struct {
	etcd         *etcd.Etcd
	Id           string
	IPAddress    string
	State        engine.JobNodeState
	electPath    string
	registerPath string

	Scheduler    *Scheduler
	Executor     *JobExecutor
	GroupManager *GroupManager
	Manager      *JobManager
	Collector    *Collector
	JobAPI       *JobAPI
	exit         chan bool
}

func NewJobNode(etcd *etcd.Etcd, ip string, id string, saver Saver) *JobNode {
	// TODO 节点的ID和ip地址取的是一个值, 是否合理?
	jobNode := &JobNode{
		etcd:         etcd,
		Id:           id,
		IPAddress:    ip,
		State:        engine.Follower,
		electPath:    JobNodeElectPath,
		registerPath: fmt.Sprintf("%s%s", JobNodeRegisterPath, id),
		exit:         make(chan bool),
	}
	jobNode.initNode()
	jobNode.Scheduler = NewScheduler(jobNode)
	jobNode.Executor = NewExecutor(jobNode)
	jobNode.GroupManager = NewGroupManager(jobNode)
	jobNode.Manager = NewJobManager(jobNode)
	//jobNode.Collector = NewCollector(jobNode, saver)
	jobNode.JobAPI = NewJobAPI(jobNode)
	return jobNode
}

func (jobNode *JobNode) initNode() {
	logrus.Infof("starting the job node server")
	jobNode.registerNode()
	jobNode.watchElectPath()
	go jobNode.tryLeader()

}

// 将自身节点注册到etcd上
func (jobNode *JobNode) registerNode() {
	txResponse, err := jobNode.tryRegister()

	if err != nil {
		logrus.Fatalf("failed to register %v", err)
	}
	if !txResponse.Success {
		logrus.Fatalf("job node:%s failed register to the path:%s", jobNode.Id, jobNode.registerPath)
	}
	logrus.Infof("job node:%s success register to the path:%s", jobNode.Id, jobNode.registerPath)
}

// 尝试成为主节点(loopTry)
func (jobNode *JobNode) tryLeader() {

RETRY:
	txResponse, err := jobNode.tryElect()

	// 如果有错误返回, 需要重试（有可能是网络请求问题）
	if err != nil {
		logrus.Infof("failed to elect %v", err)
		time.Sleep(time.Second)
		goto RETRY
	}

	// 请求成功
	if txResponse.Success {
		jobNode.State = engine.Leader
		logrus.Infof("job node:%s success elect to the path:%s", jobNode.Id, jobNode.electPath)
	} else {
		v := txResponse.Value
		if v == jobNode.Id {
			logrus.Infof("job node %s has already elect to be the leader on path %s", jobNode.Id, jobNode.electPath)
		} else {
			logrus.Infof("job node:%s failed elect to the path:%s and the job node %s is the leader now", jobNode.Id, jobNode.electPath, v)
			jobNode.State = engine.Follower
		}
	}

}

// 监测选举路径变化
func (jobNode *JobNode) watchElectPath() {
	response := jobNode.etcd.WatchKey(jobNode.electPath)
	go func() {
		event := response.Event
		for e := range event {
			switch e.Type {
			case engine.KeyCreateChangeEvent:
			case engine.KeyUpdateChangeEvent:
			case engine.KeyDeleteChangeEvent:
				go jobNode.tryLeader()
			}
		}
	}()
}

// 尝试注册
func (jobNode *JobNode) tryRegister() (*engine.TxResponse, error) {
	return jobNode.etcd.TxKeepAliveLeaseTTL(jobNode.registerPath, jobNode.Id, engine.TTL)
}

// 尝试成为主节点
func (jobNode *JobNode) tryElect() (*engine.TxResponse, error) {
	return jobNode.etcd.TxKeepAliveLeaseTTL(jobNode.electPath, jobNode.Id, engine.TTL)
}

func (jobNode *JobNode) Bootstrap() {
	<-jobNode.exit
}
